package downloader

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sync"

	"github.com/schollz/progressbar/v3"
)

// Config holds the downloader configuration
type Config struct {
	MaxThreads int
	SpeedLimit string
	OutputDir  string
	TempDir    string
	ProxyURL   string
	Resume     bool
}

// Downloader interface defines the methods that each protocol implementation must provide
type Downloader interface {
	Download(url string) error
	Stop() error
	Progress() float64
	Speed() int64
}

// BaseDownloader provides common functionality for all downloaders
type BaseDownloader struct {
	config     *Config
	progress   *progressbar.ProgressBar
	totalSize  int64
	downloaded int64
	mu         sync.Mutex
	stopChan   chan struct{}
}

// New creates a new downloader based on the protocol
func New(protocol string, config *Config) (Downloader, error) {
	switch protocol {
	case "http", "https":
		return NewHTTPDownloader(config), nil
	case "ftp", "ftps":
		return NewFTPDownloader(config), nil
	case "sftp":
		return NewSFTPDownloader(config), nil
	case "magnet", "torrent":
		return NewTorrentDownloader(config), nil
	default:
		return nil, fmt.Errorf("unsupported protocol: %s", protocol)
	}
}

func DefaultConfig() *Config {
	return &Config{
		MaxThreads: 1,
		SpeedLimit: "",
		OutputDir:  "download",
		TempDir:    "temp",
		ProxyURL:   "",
		Resume:     false,
	}
}

// initProgress initializes the progress bar
func (d *BaseDownloader) initProgress(total int64, description string) {
	d.totalSize = total
	d.progress = progressbar.NewOptions64(
		total,
		progressbar.OptionSetDescription(description),
		progressbar.OptionShowBytes(true),
		progressbar.OptionSetWidth(15),
		progressbar.OptionShowCount(),
		progressbar.OptionOnCompletion(func() {
			fmt.Println("\nDownload completed!")
		}),
	)
}

// updateProgress updates the progress bar
func (d *BaseDownloader) updateProgress(n int64) {
	d.mu.Lock()
	defer d.mu.Unlock()
	d.downloaded += n
	d.progress.Add64(n)
}

// createOutputFile creates the output file and ensures the directory exists
func (d *BaseDownloader) createOutputFile(filename string) (*os.File, error) {
	if d.config.OutputDir != "" {
		if err := os.MkdirAll(d.config.OutputDir, 0755); err != nil {
			return nil, err
		}
		filename = filepath.Join(d.config.OutputDir, filename)
	}

	return os.Create(filename)
}

func (d *BaseDownloader) createTempFile() error {
	if d.config.TempDir != "" {
		if err := os.MkdirAll(d.config.TempDir, 0755); err != nil {
			return err
		}
	}
	return nil
}

// copyWithProgress copies data from reader to writer while updating progress
func (d *BaseDownloader) copyWithProgress(dst io.Writer, src io.Reader) error {
	buf := make([]byte, 32*1024)
	for {
		nr, err := src.Read(buf)
		if nr > 0 {
			nw, err := dst.Write(buf[0:nr])
			if err != nil {
				return err
			}
			if nr != nw {
				return io.ErrShortWrite
			}
			d.updateProgress(int64(nr))
		}
		if err != nil {
			if err == io.EOF {
				// 由于 dst 类型为 io.Writer，没有 Close 方法，需要将其转换为 *os.File 类型才能调用 Close 方法
				if file, ok := dst.(*os.File); ok {
					err := file.Close()
					if err != nil {
						return err
					}
				}
				return nil
			}
			return err
		}
	}

}
